/*
 *  sokoban - a Sokoban game for midp-supporting mobile devices
 *  Copyright (C) 2007,2009 Dedi Hirschfeld
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */


package com.xomzom.midlets.sokoban;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

/**
 * The sokoban game canvas. The game canvas is a canvas that can draw 
 * the sokoban board
 * 
 * @author Dedi Hirschfeld
 */
public class SokoCanvas extends Canvas
{
    //
    // Color constants.
    //
    
    /**
     * The background color.
     */
    private final static int BACKGROUND_COLOR = 0xe0e0e0;

    /**
     * The wall color.
     */
    private final static int WALL_COLOR = 0x0000ff;
    
    /**
     * The box color.
     */
    private final static int BOX_COLOR = 0xff0000;
    
    /**
     * The box target position color.
     */
    private final static int TARGET_COLOR = 0x808080;
    
    /**
     * The player color.
     */
    private final static int PLAYER_COLOR = 0x008000;
    
    /**
     * Color for the top-left corner of the box - lighted.
     */
    private final static int LIGHTED_COLOR = 0xcfcfcf;

    /**
     * Color for the lower-right corner of the box - shaded.
     */
    private final static int SHADED_COLOR = 0x404040;


    
    //
    // Members.
    //
    
    /**
     * The game object.
     */
    private Game m_game;

    //
    // Operations.
    //
    
    /**
     * Create a canvas, associated with the given board.
     * @param game the game object this canvas is associated with.
     */
    public SokoCanvas(Game game)
    {
        m_game = game;
    }

    /**
     * Refresh the canvas.
     */
    protected void paint(Graphics g)
    {
        int left = g.getClipX();
        int top = g.getClipY();
        int scrWidth = g.getClipWidth();
        int scrHeight = g.getClipHeight();
        
        Board board = m_game.getBoard();
        int boardWidth = board.getBoardWidth();
        int boardHeight = board.getBoardHeight();

        int squareWidth = (scrWidth - left) / boardWidth;
        int squareHeight = (scrHeight - top) / board.getBoardHeight();
        // Since we want all squares to be - well, square - we need to choose
        // just one size for width and height.
        int squareRealSize = (squareWidth < squareHeight) ? 
                squareWidth : squareHeight;
        
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(left, top, scrWidth, scrHeight);
        for (int row = 0; row < boardHeight; row++)
        {
            for (int column = 0; column < boardWidth; column++)
            {
                int squareTop = top + row * squareRealSize;
                int squareLeft = left + column * squareRealSize;

                BoardSquare square = board.getSquare(column, row);
                if (square != null)
                {
                    paintSquare(squareLeft, squareTop, 
                            squareRealSize, square, g);
                }
            }
        }

        int playerX = board.getPlayerX();
        int playerY = board.getPlayerY();
        int playerTop = top + playerY * squareRealSize;
        int playerLeft = left + playerX * squareRealSize;
        
        g.setColor(PLAYER_COLOR);
        g.fillArc(playerLeft, playerTop, 
                squareRealSize - 1, squareRealSize - 1, 0, 360);

        g.setColor(0x000000);
        g.drawString("Level " + m_game.getLevel(), 
                left, top + scrHeight, Graphics.LEFT | Graphics.BOTTOM);
    }

    /**
     * Paint a square at the given position.
     * 
     * @param squareLeft The square left coordinate.
     * @param squareTop The square top coordinate.
     * @param squareSize The square width and height.
     * @param square The square to paint.
     * @param g The graphics object to draw on.
     */
    private void paintSquare(int squareLeft, int squareTop, 
                             int squareSize, BoardSquare square, 
                             Graphics g)
    {
        if (square.isWall())
        {
            draw3DBox(squareLeft, squareTop, squareSize - 1, WALL_COLOR, 
                      true, g);
        }
        else if (square.hasBox())
        {
            draw3DBox(squareLeft, squareTop, squareSize - 1, BOX_COLOR, 
                      true, g);
        }
        else if (square.isTarget())
        {
            draw3DBox(squareLeft, squareTop, squareSize - 1, TARGET_COLOR, 
                      false, g);
        }
    }
    
    /**
     * Draw a 3d box of the given color at the given position.
     * @param boxLeft the box's left coordinate.
     * @param boxTop The box's top coordinate.
     * @param size The box size
     * @param color The box's color.
     * @param outward true if the box should stick out, false if it should be
     * pressed in.
     * @param g The graphics context.
     */
    private void draw3DBox(int boxLeft, int boxTop, 
                         int size, int color, boolean outward, Graphics g)
    {
        g.setColor(color);
        g.fillRect(boxLeft, boxTop, size, size);

        int boxRight = boxLeft + size - 1;
        int boxBottom  = boxTop + size - 1;

        g.setColor(outward ? LIGHTED_COLOR : SHADED_COLOR);
        g.drawLine(boxLeft, boxTop, boxRight - 1, boxTop);
        g.drawLine(boxLeft, boxTop, boxLeft, boxBottom - 1);
        g.setColor(outward ? SHADED_COLOR: LIGHTED_COLOR);
        g.drawLine(boxLeft + 1, boxBottom, boxRight, boxBottom);
        g.drawLine(boxRight, boxTop + 1, boxRight, boxBottom);
    }

    /**
     * A callback called when a key was pressed.
     */
    protected void keyPressed(int keyCode)
    {
        super.keyPressed(keyCode);
        int gameAction = getGameAction(keyCode);
        int moveDir = -1;
        switch (gameAction)
        {
            case UP:
                moveDir = Move.DIR_UP;
                break;
            case DOWN:
                moveDir = Move.DIR_DOWN;
                break;
            case LEFT:
                moveDir = Move.DIR_LEFT;
                break;
            case RIGHT:
                moveDir = Move.DIR_RIGHT;
                break;
        }
        if (moveDir != -1)
        {
            Move move = new Move(moveDir);
            m_game.doMove(move);
        }
    }
}
